package com.zoe.onelink.ratelimit.aspect;

import com.zoe.onelink.annotation.governance.ratelimit.RateLimit;
import com.zoe.onelink.ratelimit.sepc.ApiRateLimiter;
import com.zoe.onelink.ratelimit.sepc.model.RateLimitFailureResultProvider;
import com.zoe.onelink.ratelimit.util.MethodUtil;
import com.zoe.onelink.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2022</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2022-12-06
 */
@Aspect
@RequiredArgsConstructor
@Slf4j
public class ApiRateLimitAspect {

    private final ApiRateLimiter apiRateLimiter;
    private final RateLimitFailureResultProvider rateLimitFailureResultProvider;

    @Around("@annotation(rateLimit)")
    public Object rateLimitAspect(ProceedingJoinPoint proceedingJoinPoint, RateLimit rateLimit) throws Throwable {

        // 规则资源ID
        String resourceId;
        // 优先使用注解上的资源ID,如果注解上没有配置资源ID,则使用方法签名作为资源ID
        if (StrUtil.isBlank(rateLimit.value())) {
            Signature signature = proceedingJoinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method targetMethod = methodSignature.getMethod();
            resourceId = MethodUtil.getMethodSign(targetMethod);
        } else {
            resourceId = rateLimit.value();
        }
        try {
            // 尝试获取令牌
            boolean accept = this.apiRateLimiter.accept(resourceId);
            if (!accept) {
                return this.rateLimitFailureResultProvider.getFailureResult(rateLimit.failureMsg());
            }
        } catch (Exception e) {
            log.error("[RateLimit] 限流异常: {}", e.getMessage());
        }
        return proceedingJoinPoint.proceed();
    }

}
